﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration.Install;
using System.EnterpriseServices;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Windows.Forms;
using MediaWiki.Common;
using MediaWiki.MediaWikiConverter.Utilities;
using MediaWiki.MediaWikiConverter.Utilities.Logging;
using System.Diagnostics;
using System.IO;
using System.Globalization;
using MediaWiki.MediaWikiConverter.Utilities.Logging.Writers;
using MediaWiki.MediaWikiConverter.Utilities.Registry;
using Microsoft.Win32;

namespace MediaWiki.MediaWikiConverter
{
  [RunInstaller(true)]
  public class MediaWikiInstaller : Installer
  {
    private void SetupLogging(string logName)
    {
      string logPath = GetLogPath(logName);

      if (File.Exists(logPath))
        File.Delete(logPath);

      Logger.SuppressErrors = false;
      Logger.Loggers.Add(new LogInstance
        {
          LogWriter = new TextFileWriter(logPath) { WriteIndent = true, WriteTimeStamp = true }
        });
    }

    private string GetLogPath(string logName)
    {
      string logPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
      logPath = Path.Combine(logPath, logName);

      return logPath;
    }

    public override void Install(IDictionary stateSaver)
    {
      base.Install(stateSaver);

      try
      {
        SetupLogging("InstallLog.txt");

        Logger.Trace("Installing MediaWiki", () =>
          {
            EventLogUtils.EnsureEventSourceExists(Constants.ApplicationName, "Application", ".");

            InstallComAssembly();

            CreateRegistryKeys();
          });
      }
      catch (Exception ex)
      {
        MessageBox.Show(TextExceptionFormatter.Format(ex));
      }
    }

    internal void CreateRegistryKeys()
    {
      Logger.Trace("Creating registry keys", () =>
        {
          int? version = GetInstalledWordVersion();
          Logger.Info("Installed Word version: {0}".FormatWith((version != null) ? version.ToString() : "Unknown"));

          bool is64BitWindows = Windows.Is64Bit();
          Logger.Info("Is 64bit Windows: {0}".FormatWith(is64BitWindows));
          Logger.Info("Is Wow64 Process: {0}".FormatWith(Windows.IsWow64Process()));

          // install for both version of word.. just in case
          CreateRegistryKeysForVersion(12, false);
          CreateRegistryKeysForVersion(14, false);

          if (is64BitWindows)
          {
            CreateRegistryKeysForVersion(12, true);
            CreateRegistryKeysForVersion(14, true);
          }
        });
    }

    internal void CreateRegistryKeysForVersion(int version, bool wow64)
    {
      DeleteRegistryKeysForVersion(version, wow64);

      string path = GetMediaWikiRegistryPath(version, wow64);

      Logger.Info("Creating registry key '{0}'".FormatWith(path));

      using (RegistryKeyEx mediaWikiRegistryKey = RegistryEx.LocalMachine.CreateSubKey(path, true))
      {
        mediaWikiRegistryKey.SetValue("Clsid", "{34E1E1C1-096D-4150-911F-429B6019DB94}", RegistryValueKind.String);
        mediaWikiRegistryKey.SetValue("Extensions", "txt", RegistryValueKind.String);
        mediaWikiRegistryKey.SetValue("Name", "MediaWiki", RegistryValueKind.String);
      }
    }

    private int? GetInstalledWordVersion()
    {
      RegistryKeyEx wordVersionRegistryKey = RegistryEx.ClassesRoot.OpenSubKey(@"Word.Application\CurVer");
      if (wordVersionRegistryKey != null)
      {
        string versionText = wordVersionRegistryKey.GetValue(null).ToString();
        if (!string.IsNullOrEmpty(versionText))
        {
          versionText = versionText.Substring(versionText.LastIndexOf('.') + 1);
          return Convert.ToInt32(versionText);
        }
      }

      return null;
    }

    private void InstallComAssembly()
    {
      string location = Assembly.GetExecutingAssembly().Location;
      Logger.Info("Installing assembly '{0}'".FormatWith(location));

      RegistrationHelper helper = new RegistrationHelper();
      string application = null;
      string tlb = null;
      helper.InstallAssembly(location, ref application, ref tlb, InstallationFlags.ReconfigureExistingApplication | InstallationFlags.FindOrCreateTargetApplication);

      Logger.Info("Assembly '{0}' installed with name '{1}'".FormatWith(location, application));
    }

    public override void Uninstall(IDictionary savedState)
    {
      base.Uninstall(savedState);

      try
      {
        SetupLogging("UninstallLog.txt");

        Logger.Trace("Uninstalling MediaWiki", () =>
          {
            EventLogUtils.EnsureEventSourceRemoved(Constants.ApplicationName, "Application", ".");

            UninstallComAssembly();

            DeleteRegistryKeys();
          });
      }
      catch (Exception ex)
      {
        MessageBox.Show(TextExceptionFormatter.Format(ex));
      }
    }

    internal void DeleteRegistryKeys()
    {
      Logger.Trace("Deleting registry keys", () =>
        {
          bool is64BitWindows = Windows.Is64Bit();
          Logger.Info("Is 64bit Windows: {0}".FormatWith(is64BitWindows));
          Logger.Info("Is Wow64 Process: {0}".FormatWith(Windows.IsWow64Process()));

          DeleteRegistryKeysForVersion(12, false);
          DeleteRegistryKeysForVersion(14, false);

          if (is64BitWindows)
          {
            DeleteRegistryKeysForVersion(12, true);
            DeleteRegistryKeysForVersion(14, true);
          }
        });
    }

    private string GetMediaWikiRegistryPath(int version, bool wow64)
    {
      string wow64Key = (wow64) ? @"Wow6432Node\" : string.Empty;

      string path =
        @"SOFTWARE\{0}Microsoft\Office\{1}.0\Word\Text Converters\OOXML Converters\Export\MediaWiki".FormatWith(wow64Key, version);

      return path;
    }

    internal void DeleteRegistryKeysForVersion(int version, bool wow64)
    {
      string path = GetMediaWikiRegistryPath(version, wow64);

      bool exists = (RegistryEx.LocalMachine.OpenSubKey(path, false, true) != null);

      if (exists)
      {
        Logger.Info("Deleting registry key '{0}'".FormatWith(path));
        RegistryEx.LocalMachine.DeleteSubKeyTree(path, true);
      }
    }

    private void UninstallComAssembly()
    {
      string location = Assembly.GetExecutingAssembly().Location;
      Logger.Info("Uninstalling assembly '{0}'".FormatWith(location));

      RegistrationHelper helper = new RegistrationHelper();
      helper.UninstallAssembly(location, null);
    }
  }
}